package com.dorukdestan.p2pshare.network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import com.dorukdestan.p2pshare.logger.Logger;
import com.dorukdestan.p2pshare.model.Constants;
import com.dorukdestan.p2pshare.model.Protocol;
import com.dorukdestan.p2pshare.model.Protocol.ProtocolCommand;
import com.dorukdestan.p2pshare.model.SearchResult;
import com.dorukdestan.p2pshare.storage.PeerList;
import com.dorukdestan.p2pshare.storage.SearchResults;

public class QueryServer implements Runnable{

	private Logger logger;
	
	public QueryServer() {
		logger = new Logger(getClass());
	}
	
	@Override
	public void run() {
		DatagramSocket datagramSocket = null;
			
			try {
				datagramSocket = new DatagramSocket(Constants.UDP_PORT_NUMBER);
				datagramSocket.setReuseAddress(true);
				
			} catch (SocketException e1) {
				logger.error(e1);
			}
				
			while(true){
				try {
					byte[] receivedData = new byte[datagramSocket.getReceiveBufferSize()];
					
					//Receive packets
					DatagramPacket receivedPacket = new DatagramPacket(receivedData, receivedData.length);
					
					datagramSocket.receive(receivedPacket);
					
					//Drop own packet
					InetAddress peerAddress = receivedPacket.getAddress();
//					if(MyIp.get().contains(peerAddress))
//						continue;
//FIXME burayi ac				
					//Process query
					String query = new String(
							receivedPacket.getData(), 0, receivedPacket.getLength());
					
					logger.debug("Query: " + query);
	
					//Get protocol and its arguments if any
					ProtocolCommand protocolCommand = Protocol.getProtocol(query);
					String[] protocolCommandArguments = Protocol.getProtocolArguments(query);
					
					switch (protocolCommand) {
						case PING:
							logger.debug("HELLO " + query);
							
							handleHelloCommand(peerAddress);
							break;
		
						case PONG:
							logger.debug("HELLO_BACK " + query);
							
							handleHelloBackCommand(peerAddress);
							break;
						
						case FILE_FOUND:
							logger.debug("FILE_FOUND " + query);
							if (protocolCommandArguments.length == 3) {
								
								handleFileFoundCommand(protocolCommandArguments[0],//fileHash 
														protocolCommandArguments[1], //fileLength
														protocolCommandArguments[2], //fileName
														peerAddress);
							}
							else{
								logger.warning("Found command with no arguments received and discarted.");
							}
							break;
		
						case SEARCH:
							logger.debug("SEARCH " + query);
							if (protocolCommandArguments.length > 1) {
								if(protocolCommandArguments[0].equalsIgnoreCase(Protocol.SEARCH_MODE_NAME)){
									handleSearchFileByName(protocolCommandArguments[1], peerAddress);
								}
								else if(protocolCommandArguments[0].equalsIgnoreCase(Protocol.SEARCH_MODE_HASH)){
									handleSearchFileByHash(protocolCommandArguments[1], peerAddress);
								}
								else{
									logger.warning("Search command with invalid arguments received and discarted.");
								}
							}
							else{
								logger.warning("Search command with no arguments received and discarted.");
							}
							break;
							
					case NONE:
						logger.debug("NONE " + query);
						break;
					default:
						logger.debug("Unrecognized protocol command! " + query);
						break;
				}
				
				} catch (SocketException e) {
					logger.error(e);
				} catch (UnknownHostException e) {
					logger.error(e);
				} catch (IOException e) {
					logger.error(e);
				} 
				
			}//end while
	}
	
	private void handleHelloCommand(InetAddress inetAddress){
		QueryClient.sendHelloBack(inetAddress);
	}
	
	private void handleHelloBackCommand(InetAddress inetAddress){
		PeerList.addPeer(inetAddress);
	}
	
	private void handleFileFoundCommand(String fileHash, String fileLength, String fileName, InetAddress peerAddress){
		if(SearchResults.getSearchResults().containsKey(fileHash)){
			SearchResult searchResult = SearchResults.getSearchResults().get(fileHash);
			searchResult.addPeerAddress(peerAddress);
			logger.debug("A peer added to an existing search");
		}
		else{
			//Create a new search result
			SearchResult searchResult = new SearchResult(peerAddress, Long.parseLong(fileLength), fileName);
			SearchResults.getSearchResults().put(fileHash, searchResult);
			logger.debug("New search is created");
		}
	}
	
	private void handleSearchFileByName(String fileName, InetAddress peerAddress){
		QueryClient.searchLocalAndSendResponse(Protocol.SEARCH_MODE_NAME, fileName, peerAddress);
	}
	
	private void handleSearchFileByHash(String hash, InetAddress peerAddress){
		QueryClient.searchLocalAndSendResponse(Protocol.SEARCH_MODE_HASH, hash, peerAddress);
	}
	
}
